package com.dpp.dbase.gpkg;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.os.Build;
import android.os.Environment;

import androidx.annotation.RawRes;
import androidx.annotation.RequiresApi;

import com.dpp.dbase.api.PdParent;
import com.dpp.dbase.callback.MyCallBack;
import com.dpp.dbase.result.PdResult;
import com.dpp.dbase.utils.PdMethod;
import com.esri.arcgisruntime.concurrent.ListenableFuture;
import com.esri.arcgisruntime.data.Feature;
import com.esri.arcgisruntime.data.FeatureQueryResult;
import com.esri.arcgisruntime.data.FeatureTable;
import com.esri.arcgisruntime.data.QueryParameters;
import com.esri.arcgisruntime.geometry.Geometry;
import com.esri.arcgisruntime.geometry.Polyline;
import com.esri.arcgisruntime.layers.FeatureLayer;
import com.esri.arcgisruntime.symbology.UniqueValueRenderer;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.ExecutionException;

public class PdOptGpkg extends PdParent {


    private static PdOptGpkg instance;

    public static PdOptGpkg getInstance() {
        if (instance == null) {
            instance = new PdOptGpkg();
        }
        return instance;
    }


    /**
     * 返回总的
     *
     * @return
     */
    public FeatureLayer getFeatureLayer() {
        return super.getPolygonShpLayer();
    }

    /**
     * 返回点状
     *
     * @return
     */
    public FeatureLayer getPointShpLayer() {
        return super.getPointShpLayer();
    }

    /**
     * 返回线状
     *
     * @return
     */
    public FeatureLayer getPolylineShpLayer() {
        return super.getPolylineShpLayer();
    }

    /**
     * 返回面状
     *
     * @return
     */
    public FeatureLayer getPolygonShpLayer() {
        return super.getPolygonShpLayer();
    }

    /**
     * 数据库备份或者恢复
     *
     * @param dbPath     当前数据库路径
     * @param backDbPath 需要备份到的数据库路径
     * @return
     */
    public PdResult backDb(String dbPath, String backDbPath) {
        return super.backDb(dbPath, backDbPath);
    }

    /**
     * 传入参数加载gpkg数据库，这里只有点线面返回，可以用可以不用，数据库名字也叫point polyline polygon固定
     *
     * @param dbPath              数据库路径
     * @param where               过滤参数
     * @param uniqueValueRenderer 颜色区分
     * @param loadCallBack        加载后的回调
     */
    public void loadGpkg(String dbPath, String where, UniqueValueRenderer uniqueValueRenderer, MyCallBack.loadCallBack loadCallBack) {
        super.loadGpkg(dbPath, where, uniqueValueRenderer, loadCallBack);
    }

    /**
     * 获取SQLiteDatabase db
     *
     * @param dbPath
     * @param dbName
     * @param dbVersion
     * @param context
     * @param id
     * @return SQLiteDatabase
     */
    public SQLiteDatabase getSQLiteDatabase(String dbPath, String dbName, int dbVersion, Context context, @RawRes int id) {
        return super.getSQLiteDatabase(dbPath, dbName, dbVersion, context, id);
    }

    /**
     * -----------------------------------------------------新增开始--------------------------------------------------------
     */

    /**
     * 增加多个图形
     *
     * @param featureLayer
     * @param features
     * @return
     */
    public PdResult addGpkg(FeatureLayer featureLayer, List<Feature> features) {
        try {
            semaphore.acquire();
        } catch (Exception e) {
            e.printStackTrace();
            semaphore.release();
            return PdResult.Fail("新增失败：" + e.getMessage());
        }
        ListenableFuture<Void> addFeaturesAsync = featureLayer.getFeatureTable().addFeaturesAsync(features);
        try {
            addFeaturesAsync.get();
            boolean done = addFeaturesAsync.isDone();
            if (done) {
                //记录日志
                addLogs(featureLayer, features, "add", null);
                return PdResult.success();
            } else {
                return PdResult.Fail("新增失败");
            }
        } catch (Exception e) {
            e.printStackTrace();
            return PdResult.Fail("新增失败：" + e.getMessage());
        } finally {
            semaphore.release();
        }
    }


    /**
     * 增加单个图形
     *
     * @param featureLayer
     * @param feature
     * @return
     */
    public PdResult addGpkg(FeatureLayer featureLayer, Feature feature) {
        try {
            semaphore.acquire();
        } catch (Exception e) {
            e.printStackTrace();
            semaphore.release();
            return PdResult.Fail("新增失败：" + e.getMessage());
        }
        ListenableFuture<Void> addFeatureAsync = featureLayer.getFeatureTable().addFeatureAsync(feature);
        try {
            addFeatureAsync.get();
            boolean done = addFeatureAsync.isDone();
            if (done) {
                //记录日志
                List<Feature> list = new ArrayList<>();
                list.add(feature);
                addLogs(featureLayer, list, "add", null);
                return PdResult.success();
            } else {
                return PdResult.Fail("新增失败");
            }
        } catch (Exception e) {
            return PdResult.Fail("新增失败：" + e.getMessage());
        } finally {
            semaphore.release();
        }
    }


    /**
     * -----------------------------------------------------新增结束--------------------------------------------------------
     */

    /**
     * -----------------------------------------------------修改开始--------------------------------------------------------
     */

    /**
     * 修改单个gpkg图形
     *
     * @param featureLayer
     * @param feature
     * @return
     */
    public PdResult modifyGpkgInfo(FeatureLayer featureLayer, Feature feature) {
        try {
            semaphore.acquire();
        } catch (Exception e) {
            e.printStackTrace();
            semaphore.release();
            return PdResult.Fail("修改失败：" + e.getMessage());
        }
        ListenableFuture<Void> modifyeFeature = featureLayer.getFeatureTable().updateFeatureAsync(feature);
        try {
            modifyeFeature.get();
            boolean done = modifyeFeature.isDone();
            if (done) {
                //记录日志
                List<Feature> list = new ArrayList<>();
                list.add(feature);
                addLogs(featureLayer, list, "update", "whereClause");
                return PdResult.success();
            } else {
                return PdResult.Fail("修改失败");
            }
        } catch (Exception e) {
            e.printStackTrace();
            return PdResult.Fail("修改失败：" + e.getMessage());
        } finally {
            semaphore.release();
        }
    }

    /**
     * 修改gpkg多个图形
     *
     * @param featureLayer
     * @param features
     * @return
     */
    public PdResult modifyGpkgInfo(FeatureLayer featureLayer, List<Feature> features) {
        try {
            semaphore.acquire();
        } catch (Exception e) {
            e.printStackTrace();
            semaphore.release();
            return PdResult.Fail("修改失败：" + e.getMessage());
        }
        ListenableFuture<Void> modifyFeatures = featureLayer.getFeatureTable().updateFeaturesAsync(features);
        try {
            modifyFeatures.get();
            boolean done = modifyFeatures.isDone();
            if (done) {
                //记录日志
                addLogs(featureLayer, features, "update", "whereClause");
                return PdResult.success();
            } else {
                return PdResult.Fail("修改失败");
            }
        } catch (Exception e) {
            e.printStackTrace();
            return PdResult.Fail("修改失败：" + e.getMessage());
        } finally {
            semaphore.release();
        }
    }

    /**
     * -----------------------------------------------------修改结束--------------------------------------------------------
     */

    /**
     * -----------------------------------------------------删除开始--------------------------------------------------------
     */

    /**
     * 删除Gpkg多个图形
     *
     * @param featureLayer
     * @param features
     * @param whereClause  :查询删除的参数query.getWhereClause();
     * @return
     */
    public PdResult removeGpkgInfo(FeatureLayer featureLayer, List<Feature> features, String whereClause) {
        try {
            semaphore.acquire();
        } catch (Exception e) {
            e.printStackTrace();
            semaphore.release();
            return PdResult.Fail("删除失败：" + e.getMessage());
        }
        ListenableFuture<Void> deleteFuture = featureLayer.getFeatureTable().deleteFeaturesAsync(features);

        try {
            deleteFuture.get();
            boolean done = deleteFuture.isDone();
            if (done) {
                //记录日志
                addLogs(featureLayer, features, "delete", whereClause);
                return PdResult.success();
            } else {
                return PdResult.Fail("删除失败");
            }
        } catch (Exception e) {
            e.printStackTrace();
            return PdResult.Fail("删除失败：" + e.getMessage());
        } finally {
            semaphore.release();
        }
    }

    /**
     * 删除单个Gpkg图形
     *
     * @param featureLayer
     * @param feature
     * @param whereClause  :查询删除的参数query.getWhereClause();
     * @return
     */
    public PdResult removeGpkgInfo(FeatureLayer featureLayer, Feature feature, String whereClause) {
        try {
            semaphore.acquire();
        } catch (Exception e) {
            semaphore.release();
            e.printStackTrace();
            return PdResult.Fail("删除失败：" + e.getMessage());
        }
        ListenableFuture<Void> deleteFeatureAsync = featureLayer.getFeatureTable().deleteFeatureAsync(feature);
        try {
            deleteFeatureAsync.get();
            boolean done = deleteFeatureAsync.isDone();
            if (done) {
                //记录日志
                List<Feature> list = new ArrayList<>();
                list.add(feature);
                addLogs(featureLayer, list, "delete", whereClause);
                return PdResult.success();
            } else {
                return PdResult.Fail("删除失败");
            }
        } catch (Exception e) {
            e.printStackTrace();
            return PdResult.Fail("删除失败：" + e.getMessage());
        } finally {
            semaphore.release();
        }
    }

    /**
     * 条件删除
     *
     * @param featureLayer
     * @param query
     * @return
     */
    public PdResult removeGpkgInfoByWhere(FeatureLayer featureLayer, QueryParameters query) {
        PdResult<List<Feature>> listPdResult = searchInfo(featureLayer, query);
        if (listPdResult.getStatus()) {
            List<Feature> datas = listPdResult.getData();
            if (datas.size() > 0) {
                return removeGpkgInfo(featureLayer, datas, query.getWhereClause());
            } else {
                return PdResult.Fail("删除失败，查询到删除信息返回未空");
            }
        } else {
            return PdResult.Fail("删除失败，未查询到删除信息");
        }
    }

    /**
     * -----------------------------------------------------删除结束--------------------------------------------------------
     */

    /**
     * -----------------------------------------------------查询开始--------------------------------------------------------
     */

    /**
     * 条件查询
     *
     * @param featureLayer
     * @param query        "1=1" 代表查询所有数据
     * @return
     */
    public PdResult<List<Feature>> searchInfo(FeatureLayer featureLayer, QueryParameters query) {
        try {
            semaphore.acquire();
        } catch (Exception e) {
            e.printStackTrace();
            return PdResult.Fail("查询失败：" + e.getMessage());
        }

        try {
            FeatureQueryResult features = featureLayer.selectFeaturesAsync(query, FeatureLayer.SelectionMode.NEW).get();
            Iterator<Feature> iterator = features.iterator();
            List<Feature> list = new ArrayList<>();
            while (iterator.hasNext()) {
                Feature next = iterator.next();
                list.add(next);
            }
            return PdResult.success("查询成功", list);
        } catch (Exception e) {
            e.printStackTrace();
            return PdResult.Fail("查询失败：" + e.getMessage());
        } finally {
            semaphore.release();
        }
    }


    /**
     * -----------------------------------------------------查询结束--------------------------------------------------------
     */


    //新增、删除、修改记录日志
    private void addLogs(FeatureLayer featureLayer, List<Feature> features, String type, String whereClause) {
        try {
            String tableName = featureLayer.getFeatureTable().getTableName();
            // 创建 ObjectMapper 对象
            ObjectMapper objectMapper = new ObjectMapper();

            for (Feature feature : features) {
                Map<String, Object> attributes = feature.getAttributes();
                attributes.remove("OBJECTID");
                // 将 Map 转换为 JSON 字符串
                String jsonString = objectMapper.writeValueAsString(attributes);
                Geometry geometry = feature.getGeometry();
                String geoStr = geometry.toJson();
                logOperation(tableName, type, jsonString, geoStr, whereClause);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 记录增加、修改、删除的数据gpkg
     *
     * @param dbName    数据表名
     * @param type      类型、add:新增，update:修改，delete：删除
     * @param mapString map数据
     * @param geomJson  矢量json
     */
    private void logOperation(String dbName, String type, String mapString, String geomJson, String whereClause) {
        if (!logTarget) {
            return;
        }
        FileOutputStream fos = null;
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault());
            String timestamp = sdf.format(new Date());
            String logEntry = timestamp + "-" + dbName + "-" + type + "-" + mapString + "-" + geomJson + "-" + whereClause + "\n";
            File externalDir = new File(Environment.getExternalStorageDirectory() + "/pdlogs");
            if (!externalDir.exists()) {
                externalDir.mkdirs();
            }
            File logFile = new File(externalDir, super.gpkgLogName);
            fos = new FileOutputStream(logFile, true);
            fos.write(logEntry.getBytes());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //gpkg数据恢复
    @RequiresApi(api = Build.VERSION_CODES.O)
    public void rebackData(FeatureLayer featureLayer, String beginTime, String endTime) {
        rebackGpkgDataFromLog(featureLayer, beginTime, endTime);
    }

    //gpkg数据恢复
    @RequiresApi(api = Build.VERSION_CODES.O)
    public void rebackData(FeatureLayer featureLayer) {
        rebackGpkgDataFromLog(featureLayer, null, null);
    }


    public boolean rebackGpkgDataStatus = false;//恢复状态

    /**
     * gpkg数据库恢复，读取日志恢复数据
     *
     * @param featureLayer
     * @param beginTime    年月日 yyyy/MM/dd HH:mm:ss
     * @param endTime      年月日 yyyy/MM/dd HH:mm:ss
     */
    @RequiresApi(api = Build.VERSION_CODES.O)
    private void rebackGpkgDataFromLog(FeatureLayer featureLayer, String beginTime, String endTime) {
        try {
            rebackGpkgDataStatus = false;//恢复状态
            File externalDir = Environment.getExternalStorageDirectory();
            File logFile = new File(externalDir, super.gpkgLogName);
            if (logFile.exists()) {
                java.util.Scanner scanner = new java.util.Scanner(logFile);
                String tableName = featureLayer.getFeatureTable().getTableName();
                // 创建 ObjectMapper 对象
                ObjectMapper objectMapper = new ObjectMapper();
                while (scanner.hasNextLine()) {
                    try {
                        String line = scanner.nextLine();
                        String[] split = line.split("-");
                        boolean target = true;
                        String trim = split[0].trim();
                        long lg = convertToLong(trim);
                        if (beginTime != null) {
                            long beginLg = convertToLong(beginTime);
                            if (lg < beginLg) {
                                target = false;
                            }
                        }
                        if (endTime != null) {
                            long endlg = convertToLong(endTime);
                            if (lg > endlg) {
                                target = false;
                            }
                        }
                        if (tableName.equalsIgnoreCase(split[1].trim()) && target) {
                            String type = split[2].trim();//add ，update ,delete
                            String mapString = split[3];
                            // 将 JSON 字符串转换回 Map
                            Map<String, Object> convertedMap = objectMapper.readValue(mapString, HashMap.class);
                            String geomJson = split[4];
                            Geometry geometry = Geometry.fromJson(geomJson);
                            List<Feature> opt = new ArrayList<>();
                            Feature feature = featureLayer.getFeatureTable().createFeature(convertedMap, geometry);
                            if ("add".equalsIgnoreCase(type)) {
                                try {
                                    semaphore.acquire();
                                    featureLayer.getFeatureTable().addFeatureAsync(feature).get();
                                } finally {
                                    semaphore.release();
                                }

                            } else if ("delete".equalsIgnoreCase(type)) {
                                String whereClause = split[5].trim();
                                QueryParameters queryParameters = new QueryParameters();
                                queryParameters.setWhereClause(whereClause);
                                PdResult<List<Feature>> listPdResult = searchInfo(featureLayer, queryParameters);
                                if (listPdResult.getStatus()) {
                                    List<Feature> data = listPdResult.getData();
                                    boolean b = data.size() > 0;
                                    if (b) {
                                        try {
                                            for (Feature f : data) {
                                                f.setGeometry(geometry);
                                                f.getAttributes().putAll(convertedMap);
                                                opt.add(f);
                                            }
                                            semaphore.acquire();
                                            featureLayer.getFeatureTable().deleteFeaturesAsync(opt).get();
                                        } finally {
                                            semaphore.release();
                                        }
                                    }
                                }

                            } else if ("update".equalsIgnoreCase(type)) {
                                String whereClause = split[5].trim();
                                QueryParameters queryParameters = new QueryParameters();
                                queryParameters.setWhereClause(whereClause);
                                PdResult<List<Feature>> listPdResult = searchInfo(featureLayer, queryParameters);
                                if (listPdResult.getStatus()) {
                                    List<Feature> data = listPdResult.getData();
                                    boolean b = data.size() > 0;
                                    if (b) {
                                        try {
                                            for (Feature f : data) {
                                                f.setGeometry(geometry);
                                                f.getAttributes().putAll(convertedMap);
                                                opt.add(f);
                                            }

                                            semaphore.acquire();
                                            featureLayer.getFeatureTable().updateFeaturesAsync(opt).get();
                                        } finally {
                                            semaphore.release();
                                        }
                                    }
                                }
                            }

                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                scanner.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            rebackGpkgDataStatus = true;//恢复状态
        }
    }


    //清楚数据库备份
    public void cleanGpkgBack() {
        try {
            File externalDir = Environment.getExternalStorageDirectory();
            File file = new File(externalDir, super.gpkgLogName);
            if (file.isFile() && file.exists()) {
                file.delete();
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.O)
    private long convertToLong(String timestampStr) {
        String format = "yyyy/MM/dd HH:mm:ss";
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(format);
        LocalDateTime dateTime = LocalDateTime.parse(timestampStr, formatter);
        return dateTime.toInstant(ZoneOffset.UTC).toEpochMilli();
    }
}
